﻿using Microsoft.Extensions.Hosting;
using Polly;
using zy.webcore.Share.Redis.CacheProvider;
using zy.webcore.Share.Redis.Units;

namespace zy.webcore.Share.Application.Caching
{
    public abstract class AbstractCacheService :  ICachePreheatable
    {
        protected virtual Lazy<ICacheProvider> CacheProvider { get; private set; }
        protected virtual Lazy<IServiceProvider> ServiceProvider { get; private set; }
        protected virtual Lazy<IObjectMapper> Mapper { get; private set; }

        protected AbstractCacheService(Lazy<ICacheProvider> cacheProvider, Lazy<IServiceProvider> serviceProvider)
        {
            CacheProvider = cacheProvider;
            ServiceProvider = serviceProvider;
            Mapper = ServiceProvider.Value.GetRequiredService<Lazy<IObjectMapper>>();
        }

        public abstract Task PreheatAsync();

        /// <summary>
        /// 连接KEY
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public virtual string ConcatCacheKey(params object[] items)
        {
            if (items==null||items.Length==0)
                throw new ArgumentNullException(nameof(items));
            return string.Join(":", items);
        }
        /// <summary>
        /// 移除cache
        /// </summary>
        /// <param name="dataOperater"></param>
        /// <param name="cacheKeys"></param>
        /// <returns></returns>
        public virtual async Task RemoveCachesAsync(Func<CancellationToken, Task> dataOperater, params string[] cacheKeys)
        {
            var pollyTimeoutSeconds = CacheProvider.Value.CacheOptions.Value.PollyTimeoutSeconds;
            var keyExpireSeconds = pollyTimeoutSeconds + 1;

            await CacheProvider.Value.KeyExpireAsync(cacheKeys, keyExpireSeconds);

            var expireDt = DateTime.Now.AddSeconds(keyExpireSeconds);
            var cancelTokenSource = new CancellationTokenSource();
            var timeoutPolicy = Policy.TimeoutAsync(pollyTimeoutSeconds, Polly.Timeout.TimeoutStrategy.Optimistic);
            await timeoutPolicy.ExecuteAsync(async (cancellToken) =>
            {
                await dataOperater(cancellToken);
                cancellToken.ThrowIfCancellationRequested();
            }, cancelTokenSource.Token);

            try
            {
                await CacheProvider.Value.RemoveAllAsync(cacheKeys);
            }
            catch (Exception ex)
            {
                LocalVariables.Instance.Queue.Enqueue(new LocalVariables.Model(cacheKeys, expireDt));
                var logger = ServiceProvider.Value.GetRequiredService<Lazy<ILogger<AbstractCacheService>>>();
                logger.Value.LogError(ex, ex.Message);
            }
        }
    }
}
